Grid search CV

머신 러닝에 사용되는 두 종류의 파라미터
- 훈련 데이터에서 학습되는 파라미터
    ex) 로지스터 회귀의 가중치
- 최적화되는 학습 알고리즘의 파라미터(모델의 튜닝 파라미터) == 하이퍼파라미터
    ex) 로지스틱 회귀의 규제 매개변수나 결정트리의 깊이 매개변수

그리드 서치는 하이퍼파라미터 최적화 기법으로 하이퍼파라미터 값에 대한 최적의 조합을 찾아 모델 성능을 향상시키는 튜닝 방법이다.

리스트로 지정된 여러 가지 하이퍼파라미터 값 전체를 조사한다.
이 리스트에 있는 값의 모든 조합에 대해 모델 성능을 평가하여 최적의 조합을 찾는다.
from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC
pipe_svc=make_pipeline(StandardScaler(), SVC(random_state=1))
param_range=[0.0001, 0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0]
param_grid=[{'svc__C': param_range, 'svc__kernel':['linear']}, {'svc__C':param_range, 'svc__gamma':param_range, 'svc__kernel':['rbf']}]
gs=GridSearchCV(estimator=pipe_svc, param_grid=param_grid, scoring='accuracy', cv=10, refit=True, n_jobs=-1)
gs=gs.fit(X_train, y_train)
print(gs.best_score_)
print(gs.best_params_)

0.9846859903381642

{'svc__C': 100.0, 'svc__gamma': 0.001, 'svc__kernel': 'rbf'}

parma_grid에 튜닝할려는 매개변수를 딕셔너리의 리스트로 지정
위의 코드는 linear SVC의 경우 매개변수 C만
RBF 커널 SVM의 경우 svc__c, svc__gamma 매개변수를 튜닝

훈련데이터를 통해서 그리드 서치를 수행한 후, 최상의 모델 점수는 best_score_ 속성에서
이 모델의 매개변수는 best_params_ 속성에서 확인할 수 있다.
독립적인 테스트 데이터셋을 통한 최고의 모델 성능 추정
clf=gs.best_estimator_
clf.fit(X_train, y_train)
print(' : %.3f' %clf.score(X_test, y_test))

테스트 정확도: 0.974

GridSearchCV 객체의 best_estimator_ 속성으로 테스트 데이터셋을 위한 모델을 얻을 수 있다.
이후 fit으로 통해 최상의 매개변수 조합과 훈련 데이터셋에서 모델(gs.best_estimator_)을 수동으로 훈련할 필요 없다.
GridSearchCV 클래스의 refit 매개변수를 True로 지정하면 전체 훈련 데이터셋에서 자동으로 gs.best_estimator_를 다시 훈련
default value is True
GridSearchCV 클래스와 cross_validation 함수에서 return_train_score 매개변수를 True로 지정하면,
훈련 폴드에 대한 점수를 계산하여 반환한다.
param_range에서 8개의 값이 지정되어 있기 때문에, linear에서 8번 RBF에서 64번의 교차 검증이 실행된다.
따라서 반환되는 점수는 총 72개이다.
cv_results_ 딕셔너리 속성에 split{fold_num}_train_score와 같은 키에 저장(split0_train_score->첫번째 폴드 점수)
gs=GridSearchCV(estimator=pipe_svc, param_grid=param_grid, scoring='accuracy', cv=10, return_train_score=True, n_jobs=-1)
gs=gs.fit(X_train, y_train)
gs.cv_results_['split0_train_score']

array([0.6405868 , 0.93643032, 0.97555012, 0.98777506, 0.98533007,

       0.99266504, 0.99755501, 1.        , 0.62591687, 0.62591687,

       0.62591687, 0.62591687, 0.62591687, 0.62591687, 0.62591687,

       0.62591687, 0.62591687, 0.62591687, 0.62591687, 0.62591687,

       0.62591687, 0.62591687, 0.62591687, 0.62591687, 0.62591687,

       0.62591687, 0.62591687, 0.62591687, 0.62591687, 0.62591687,

       0.62591687, 0.62591687, 0.62591687, 0.7799511 , 0.94621027,

       0.96577017, 0.62591687, 0.62591687, 0.62591687, 0.62591687,

       0.78484108, 0.94621027, 0.9804401 , 0.99266504, 1.        ,

       1.        , 1.        , 1.        , 0.94621027, 0.97799511,

       0.99266504, 1.        , 1.        , 1.        , 1.        ,

       1.        , 0.97799511, 0.98777506, 0.99511002, 1.        ,

       1.        , 1.        , 1.        , 1.        , 0.98533007,

       0.99266504, 1.        , 1.        , 1.        , 1.        ,

       1.        , 1.        ])

전체 훈련 점수의 평균값
gs.cv_results_['mean_train_score']

array([0.6402928 , 0.93724074, 0.97240801, 0.98510406, 0.98803447,

       0.99145447, 0.99707019, 0.9992677 , 0.62637307, 0.62637307,

       0.62637307, 0.62637307, 0.62637307, 0.62637307, 0.62637307,

       0.62637307, 0.62637307, 0.62637307, 0.62637307, 0.62637307,

       0.62637307, 0.62637307, 0.62637307, 0.62637307, 0.62637307,

       0.62637307, 0.62637307, 0.62637307, 0.62637307, 0.62637307,

       0.62637307, 0.62637307, 0.62637307, 0.77070249, 0.94700817,

       0.97167094, 0.62637307, 0.62637307, 0.62637307, 0.62637307,

       0.77949371, 0.94725326, 0.97704753, 0.99291848, 1.        ,

       1.        , 1.        , 1.        , 0.94652096, 0.97753354,

       0.99023257, 1.        , 1.        , 1.        , 1.        ,

       1.        , 0.97680064, 0.98852287, 0.99755799, 1.        ,

       1.        , 1.        , 1.        , 1.        , 0.98803387,

       0.99291848, 1.        , 1.        , 1.        , 1.        ,

       1.        , 1.        ])

첫 번째 폴드에 대한 테스트 점수 ‘split0_test_score’
gs.cv_results_['split0_test_score']

array([0.63043478, 0.89130435, 0.95652174, 0.97826087, 0.95652174,

       0.93478261, 0.95652174, 0.93478261, 0.63043478, 0.63043478,

       0.63043478, 0.63043478, 0.63043478, 0.63043478, 0.63043478,

       0.63043478, 0.63043478, 0.63043478, 0.63043478, 0.63043478,

       0.63043478, 0.63043478, 0.63043478, 0.63043478, 0.63043478,

       0.63043478, 0.63043478, 0.63043478, 0.63043478, 0.63043478,

       0.63043478, 0.63043478, 0.63043478, 0.69565217, 0.93478261,

       0.95652174, 0.63043478, 0.63043478, 0.63043478, 0.63043478,

       0.69565217, 0.93478261, 0.93478261, 1.        , 0.63043478,

       0.63043478, 0.63043478, 0.63043478, 0.93478261, 0.97826087,

       1.        , 1.        , 0.63043478, 0.63043478, 0.63043478,

       0.63043478, 0.97826087, 0.97826087, 0.97826087, 1.        ,

       0.63043478, 0.63043478, 0.63043478, 0.63043478, 0.97826087,

       0.95652174, 0.95652174, 1.        , 0.63043478, 0.63043478,

       0.63043478, 0.63043478])

gs.score & gs.predict & gs.transform
print(' : %.3f' %gs.score(X_test, y_test))

테스트 정확도: 0.974

randomized search
그리드 서치는 최적의 매개변수를 찾을 수 있는 강력한 도구이지만 모든 매개변수 조합을 평가하기 위한
계산 비용이 높다.
여러 가지 매개변수 조합을 샘플링하는 다른 방법으로 랜덤 서치(randomized search)가 있다.
랜덤 서치는 그리드 서치와 비슷한 성능을 내지만, 훨씬 비용과 시간이 적게 든다.
랜덤 서치에서 60개의 파라미터가 있다면, 최적의 성능에서 5% 이내에 있는 솔루션을 얻을 확률이 95%이다.

제한된 횟수 안에서 샘플링 분포로부터 랜덤한 매개변수 조합을 뽑는다.
매개변수 탐색 범위가 넓거나, 규제 매개변수 C처럼 연속적인 값을 탐색해야 하는 경우에 RandomizedSearchCV가 더 효율적이다.

n_iter 매개변수로 탐색 횟수를 조정할 수 있어 컴퓨팅 자뤈에 맞게 탐색을 실행할 수 있다.

그리드 서치에서 탐색할 매개변수를 리스트로 전달했다면 랜덤 서치에서는 샘플링 가능한 분포를 지정해 줘야 한다.
ex) scipy.stats.uniform, scipy.stats.randint, scipy.stats.reciprocal 등
from sklearn.model_selection import RandomizedSearchCV
from sklearn.utils.fixes import loguniform
distribution=loguniform(0.0001, 1000.0)
param_dist=[{'svc__C':distribution, 'svc__kernel':['linear']}, {'svc__C':distribution, 'svc__gamma':distribution, 'svc__kernel':['rbf']}]
rs=RandomizedSearchCV(estimator=pipe_svc, param_distributions=param_dist, n_iter=30, cv=10, random_state=1, n_jobs=-1)
rs=rs.fit(X_train, y_train)
print(rs.best_score_)
print(rs.best_params_)

0.9824637681159419

{'svc__C': 210.6644070836221, 'svc__gamma': 0.0006861724481510375, 'svc__kernel': 'rbf'}

탐색 횟수는 절반 이상 적지만, 더 좋은 성능을 내는 매개변수 조합을 찾는다.
HalvingGridSearchCV(SH)
모든 파라미터 조합에 대해 제한된 자원으로 실행한 후 가장 좋은 후보를 골라서 더 많은 자원을 투여하는 식으로
반복적으로 탐색을 수행

resource 매개변수는 반복마다 늘려 갈 자원을 정의한다. default 값은 ’n_samples’로 샘플 개수이다.
양의 정수 값을 가진 매개변수를 지정할 수 있다. ex) 랜덤 포레스트의 n_estimators 사용 가능


factor 매개변수는 반복마다 선택할 후보의 비율을 지정한다.
default 값은 3으로 1/3만 다음으로 전달

max_resources 매개변수는 각 후보가 사용할 최대 자원을 지정한다.
default 값은 ‘auto’, resource 매개변수가 n_samples 일 때, max_resources는 샘플 개수가 된다.

min_resources 매개변수는 첫 번째 반복에서 각 후보가 사용할 최소 자원을 지정
resources=’n_samples’ 이고 min_resources=‘smallest’이면
회귀일 때, cvX2가 되고 분류일 때는 cvX클래스 개수X2가 된다. 그외에는 1
min_resources=‘exhaust’이면 앞에 계산한 값과 max_resources를 factor**n_resquired_iterations로
나눈 몫 중 큰 값
default 값은 exhaust이다.

aggressive_elimination 매개변수를 True로 지정하면, 마지막 반복에서 factor마큼 후보가 남을 수 있도록
자원을 늘리지 않고 초기에 반복을 여러번 진행
default 값은 False이다.

verbose=1로 지정하면 반복 과정을 상세히 출력한다.
HalvingGridSearchCV는 아직 실험적임
from sklearn.experimental import enable_halving_search_cv
from sklearn.model_selection import HalvingGridSearchCV
hgs=HalvingGridSearchCV(estimator=pipe_svc, param_grid=param_grid, cv=10, n_jobs=-1, verbose=1)
hgs=hgs.fit(X_train,y_train)
print(hgs.best_score_)
print(hgs.best_params_)

n_iterations: 3

n_required_iterations: 4

n_possible_iterations: 3

min_resources_: 40

max_resources_: 455

aggressive_elimination: False

factor: 3

----------

iter: 0

n_candidates: 72

n_resources: 40

Fitting 10 folds for each of 72 candidates, totalling 720 fits

----------

iter: 1

n_candidates: 24

n_resources: 120

Fitting 10 folds for each of 24 candidates, totalling 240 fits

----------

iter: 2

n_candidates: 8

n_resources: 360

Fitting 10 folds for each of 8 candidates, totalling 80 fits


0.983095238095238


{'svc__C': 1000.0, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf'}

첫번째 반복에서 72개의 후보를 40개의 샘플로 교차검증
72/3=24개의 후보를 뽑아 두 번째 반복을 수행

총 104번의 교차 검증을 진행한다.

cv_results_ 속성의 mean_fit_time에 교차 검증에 걸린 시간이 저장되어 있다.
이는 GridSearchCV보다 5배 이상 빠르다.
import numpy as np
print(np.sum(hgs.cv_results_['mean_fit_time']))

0.1161189556121826

각 반복에서 사용한 샘플과 후보의 개수는 각각 n_resources_속성과 n_candidates_속성에 저장
print(' :', hgs.n_resources_)
print(' :', hgs.n_candidates_)

자원 리스트: [40, 120, 360]

후보 리스트: [72, 24, 8]